%output="miparser.cpp"

%{
#include "midef.h"

#ifdef UNITTEST
#include <stdio.h>

void	PrintGDBOutput (MIGDBOutput * output);
#endif

MIGDBOutput * ParseMIOutput (const char * output);

int		yylex ();
void	yyerror (char const *);
void	InitLexer (const char * input);

//MIResult * ReverseResults(MIResult* head); //add by porky 200903

static MIGDBOutput * s_pResult;

%}

%union {
	MIGDBOutput			* pGDBOutput;
	MIOutOfBandRecord	* pOutOfBandRecord;
	MIAsyncRecord		* pAsyncRecord;
	MIStreamRecord		* pStreamRecord;
	MIResultRecord		* pResultRecord;
	MIResult			* pResult;
	MIValue				* pValue;
	MIList				* pList;
	MIString			strval;
	int					intval;
	MIAsyncClass		async_class;
	MIResultClass		result_class;
}

%token MI_NL MI_GDB

%token <strval> MI_CSTRING
%token <strval> MI_STRING
%token <intval> MI_TOKEN
%token <result_class> MIC_DONE MIC_RUNNING MIC_CONNECTED MIC_ERROR MIC_EXIT
%token <async_class> MIC_STOPPED
%type <pGDBOutput> output
%type <intval> token
%type <strval> const
%type <result_class> result_class
%type <async_class> async_class
%type <pOutOfBandRecord> out_of_band_record
%type <pResultRecord> result_record result_output
%type <pResult> results result tuple
%type <pList> list
%type <pAsyncRecord> async_record exec_async_output status_async_output notify_async_output async_output
%type <pValue> value values
%type <pStreamRecord> console_stream_output target_stream_output log_stream_output stream_record

%%

migrammer				:	output							{	s_pResult = $1;	}
						;
output					:	MI_GDB MI_NL					{	$$ = new MIGDBOutput; 
																$$->m_pOutOfBandRecord = 0;
																$$->m_pResultRecord = 0;	}
						|	result_record MI_GDB MI_NL		{	$$ = new MIGDBOutput;
																$$->m_pOutOfBandRecord = 0;
																$$->m_pResultRecord = $1;	}
						|	out_of_band_record output		{	$1->m_pNext = $2->m_pOutOfBandRecord;
																$2->m_pOutOfBandRecord = $1;
																$$ = $2;	}										
						;
result_record			:	token result_output				{	$2->m_token = $1; $$ = $2;	}
						|	result_output					{	$1->m_token = 0; $$ = $1;	}
						;
result_output			:	'^' result_class results MI_NL	{	$$ = new MIResultRecord;
																$$->m_class = $2;
																$$->m_pResult = $3;} //ReverseResults($3);	}
						;
//results				:	results ',' result 				{	$3->m_pNext = $1; $$ = $3;	}
//						|	/* null */						{	$$ = 0;	}
//						;
results					:	 ',' result results				{	$2->m_pNext = $3; $$ = $2;	}
						|	/* null */						{	$$ = 0;	}
						;
out_of_band_record		:	async_record					{	$$ = new MIOutOfBandRecord; $$->m_pNext = 0;
																$$->m_pAsyncRecord = $1;
																$$->m_type = ASYNC;	}
						|	stream_record					{	$$ = new MIOutOfBandRecord; $$->m_pNext = 0;
																$$->m_pStreamRecord = $1;
																$$->m_type = STREAM;	}
						;
async_record			:	exec_async_output				{	$1->m_type = EXEC; $$ = $1;	}
						|	status_async_output				{	$1->m_type = STATUS; $$ = $1;	}
						|	notify_async_output				{	$1->m_type = NOTIFY; $$ = $1;	}
						;
exec_async_output		:	token '*' async_output			{	$3->m_token = $1; $$ = $3;	}
						|	'*' async_output				{	$2->m_token = 0; $$ = $2;	}
						;
status_async_output		:	token '+' async_output			{	$3->m_token = $1; $$ = $3;	}
						|	'+' async_output				{	$2->m_token = 0; $$ = $2;	}
						;
notify_async_output		:	token '=' async_output			{	$3->m_token = $1; $$ = $3;	}
						|	'=' async_output				{	$2->m_token = 0; $$ = $2;	}
						;
async_output			:	async_class results MI_NL		{	$$ = new MIAsyncRecord;
																$$->m_pResult = $2;//ReverseResults($2);
																$$->m_class = $1;	}
						;
result_class			:	MIC_DONE						{	$$ = DONE;	}
						|	MIC_RUNNING						{	$$ = RUNNING;	}
						|	MIC_CONNECTED					{	$$ = CONNECTED;	}
						|	MIC_ERROR						{	$$ = ERROR;	}
						|	MIC_EXIT						{	$$ = EXIT;	}
						;
async_class				:	MIC_STOPPED						{	$$ = STOPPED;	}
						|	MI_STRING		/* something added furhter */
															{	$$ = OTHER;	}
						;
result					:	MI_STRING '=' value				{	$$ = new MIResult; $$->m_pNext = 0; $$->m_variable = $1;
																$$->m_pValue = $3;	}
						;
value					:	const							{	$$ = new MIValue; $$->m_pNext = 0; $$->m_type = CONST; $$->m_string = $1;	}
						|	tuple							{	$$ = new MIValue; $$->m_pNext = 0; $$->m_type = TUPLE; $$->m_pResult = $1;	}
						|	list							{	$$ = new MIValue; $$->m_pNext = 0; $$->m_type = LIST; $$->m_pList = $1;	}
						;
values					:	',' value values				{	$2->m_pNext = $3; $$ = $2;	}
						|	/* null */						{	$$ = 0;	}
						;
const					:	MI_CSTRING						{	$$ = $1;	}
						;
tuple					:	'{' '}'							{	$$ = 0;	}
						|	'{' result results '}'			{	$2->m_pNext = $3;$$ = $2; }//ReverseResults($3); $$ = $2;	}
						;
list					:	'[' ']'							{	$$ = new MIList; $$->m_type = EMPTY;	}
						|	'[' value values ']'			{	$$ = new MIList; $$->m_type = VALUE;
																$2->m_pNext = $3; $$->m_pValue = $2;	}
						|	'[' result results ']'			{	$$ = new MIList; $$->m_type = RESULT;
																$2->m_pNext = $3; $$->m_pResult = $2;} //ReverseResults($3); $$->m_pResult = $2;}
						;
stream_record			:	console_stream_output			{	$$ = $1;	}
						|	target_stream_output			{	$$ = $1;	}
						|	log_stream_output				{	$$ = $1;	}
						;
console_stream_output	:	'~' MI_CSTRING MI_NL			{	$$ = new MIStreamRecord; 
																$$->m_type = CONSOLE;
																$$->m_string = $2;	}
						;
target_stream_output	:	'@' MI_CSTRING MI_NL			{	$$ = new MIStreamRecord; 
																$$->m_type = TARGET;
																$$->m_string = $2;	}
						;
log_stream_output		:	'&' MI_CSTRING MI_NL			{	$$ = new MIStreamRecord;
																$$->m_type = LOG;
																$$->m_string = $2;	}
						;
token					:	MI_TOKEN						{	$$ = $1;	}
						;

%%

void yyerror (char const * s)
{
#ifdef UNITTEST
	fprintf (stderr, "%s", s);
#endif
}

MIGDBOutput * ParseMIOutput (const char * output)
{
	InitLexer (output);
	s_pResult = 0;
	
	return yyparse () == 0 ? s_pResult : 0;
}

/* 
MIResult * ReverseResults(MIResult* head){

	if(head == 0) return 0;
	MIResult * point1,*point2;
	//first node
	point1 = head->m_pNext;
	if(point1 == 0) return head;
	point2 = point1->m_pNext;	
	point1 ->m_pNext = head;
	head->m_pNext = 0;
	head = point1;
	
	
	while(point2){
		point1 = point2;
		point2 = point2->m_pNext;
		point1 -> m_pNext = head;
		head = point1;
	}
	return head;
}
*/

#ifdef UNITTEST
void PrintList (MIList * list, int tab);
void PrintValue (MIValue * value, int tab);
void PrintResult (MIResult * result, int tab);
void PrintGDBOutput (MIGDBOutput * output);

int main (int argc, char ** argv)
{	
	int i;
	int fail = 0;
	char * buffer = (char *)malloc (16 * 1024);
	
	for (i = 1; i < argc; ++i)
	{
		printf ("process %s...", argv[i]);
		
		FILE * fp = fopen (argv[i], "r");
		int c = 0;
		
		if (! fp)
		{
			printf ("failed to open %s\n", argv[i]);
			continue;
		}
		
		while ((buffer[c] = fgetc (fp)) != EOF)
			++c;
		buffer[c] = 0;
		printf ("%d characters read\n", c);
		
		MIGDBOutput * output = ParseMIOutput (buffer);
		if (output)
		{
			PrintGDBOutput (output);
			ReleaseMIOutput (output);
		}
		else
			++ fail;
			
		fclose (fp);
	}

	free (buffer);
	
	printf ("**************************************************\n");
	printf ("Result: %d fail\n", fail);
	
	return 0;
}

char * GetTabs (int tab)
{
	char * tabs = (char *)malloc (tab + 1);
	for (int i = 0; i < tab; i++)
		tabs[i] = ' ';
	tabs[tab] = '\0';

	return tabs;
}

void PrintList (MIList * list, int tab)
{
	char * tabs = GetTabs (tab);

	printf ("%s<List>\n", tabs);

	if (list->m_type == VALUE)
		PrintValue (list->m_pValue, tab + 1);
	else if (list->m_type == RESULT)
		PrintResult (list->m_pResult, tab + 1);

	printf ("%s</list>\n", tabs);

	free (tabs);
}

void PrintValue (MIValue * value, int tab)
{
	char * tabs = GetTabs (tab);

	while (value)
	{
		printf ("%s<Value>\n", tabs);

		if (value->m_type == CONST)
			printf ("%s <const>%s</const>\n", tabs, value->m_string);
		else if (value->m_type == TUPLE)
			PrintResult (value->m_pResult, tab + 1);
		else // value->m_type == LIST
			PrintList (value->m_pList, tab + 1);

		printf ("%s</Value>\n", tabs);

		value = value->m_pNext;
	}

	free (tabs);
}

void PrintResult (MIResult * result, int tab)
{
	char * tabs = GetTabs (tab);

	while (result)
	{
		printf ("%s<Result>\n%s <variable>%s</variable>\n",
			tabs, tabs, result->m_variable);
		PrintValue (result->m_pValue, tab + 1);
		printf ("%s</Result>\n", tabs);

		result = result->m_pNext;
	}

	free (tabs);
}

void PrintGDBOutput (MIGDBOutput * output)
{
	printf ("<MIGDBOutput>\n");

	MIOutOfBandRecord * pOutOfBandRecord = output->m_pOutOfBandRecord;
	while (pOutOfBandRecord)
	{
		printf (" <OutOfBandRecord>\n");
		if (pOutOfBandRecord->m_type == ASYNC)
		{
			printf ("  <type>%d</type>\n  <token>%d</token>\n  <class>%d</class>\n",
				pOutOfBandRecord->m_pAsyncRecord->m_type,
				pOutOfBandRecord->m_pAsyncRecord->m_token,
				pOutOfBandRecord->m_pAsyncRecord->m_class);
			PrintResult (pOutOfBandRecord->m_pAsyncRecord->m_pResult, 3);
		}
		else // STREAM
		{
			printf ("  <type>%d</type>\n  <string>%s</string>\n",
				pOutOfBandRecord->m_pStreamRecord->m_type,
				pOutOfBandRecord->m_pStreamRecord->m_string);
		}
		printf (" </OutOfBandRecord>\n");

		pOutOfBandRecord = pOutOfBandRecord->m_pNext;
	}

	if (output->m_pResultRecord)
	{
		printf (" <ResultRecord>\n  <class>%d</class>\n  <token>%d</token>\n",
			output->m_pResultRecord->m_class, output->m_pResultRecord->m_token);
		PrintResult (output->m_pResultRecord->m_pResult, 1);
		printf ("</ResultRecord>\n");
	}
	
	printf ("</MIGDBOutput>\n");
}
#endif